/*
Hardware initialization code

PORT MAPPING:
B0 Current Sensor 1 (Input) - ADC0
B1 Current Sensor 2 (Input) - ADC1
B2 High Voltage Sense Input - ADC2
B3 Motor Temperature Input - ADC3
B4 Quadrature Encoder Input A (input)
B5 Quadrature Encoder Input B (input)
B6 Throttle Position (Input) - ADC6
B7 Throttle Position 2 (Input) - ADC7
B8 Controller Temperature Input - ADC8

C13 Precharge Contactor
C14 Main Contactor
C15 Oscillator pin 2

D0 Driver Fault Input INT1
D1 Over current trip fault (input) INT2
D2 unused 
D3 Drive Enable / OverCurr Reset (DIGITAL IN)

E0 PWM 1 Low Side (output)
E1 PWM 1 High Side (output)
E2 PWM 2 Low Side (output)
E3 PWM 2 High Side (output)
E4 PWM 3 Low Side (output)
E5 PWM 3 High Side (output)
E8 unused (FAULT A, INT0)

F0 canbus RX (input)
F1 canbus TX (output)
F2 unused / PGC / UART1 RX (Input)
F3 unused / PGD / UART1 TX (output)
F4 UART2 RX (input)
F5 UART2 TX (output)
F6 Status LED
*/


#include <p30F4011.h>
#include "settings.h"
#include "AC-Controller.h"
#include "globals.h"
#include "eeprom.h"


void InitIORegisters() {

	SET_OUTPUT(_TRISC13); //precharge contactor
	SET_LOW(PRECHARGE_RELAY); //I've done relays with positive logic so LOW means OFF

	SET_OUTPUT(_TRISC14);
	SET_LOW(CONTACTOR_RELAY); //main relay is LOW OFF too 

	SET_OUTPUT(_TRISF6); //status LED
	SET_HIGH(STATUS_LED); //starts on which is high

	SET_OUTPUT(_TRISD3);   // Output for over current trip reset.

	SET_INPUT(_TRISD0);  // Set the pin as an INPUT.  overcurrent trip state.  Has an overcurrent event happened?
	// You can't set OVER_CURRENT_TRIP_STATE to anything, since it's an input!  HAHA.
	// OVER_CURRENT_TRIP_STATE is 0 if NO overcurrent event has occurred.  It is 1 if an overcurrent event HAS happened.
	
	MAXCNT = 0xFFFF; // Use this for the AC controller.  It's easier to measure speed of rotor with this setting, if there's no INDEX signal on the encoder.

	//POSCNT=0;  // How many ticks have gone by so far?  Starts out as zero anyway.
	return;
}


/*the goal is to shoot for 16kHz timer interrupt That's 16 times per millisecond which should be plenty
//the base clock could be a number of values so we'll try the best we can to make it all line up
//if the base clock is 64Mhz then the instruction clock is 16Mhz and 65536 counts is 4ms or 250Hz
//we want 64 times that speed which equates to a PR1 time of about 1024
*/
void InitTimer1() {
	T1CON = 0b1000000000000000;
    //15 = TON (Timer on)
	//13 = TSIDL (Stops in Idle? 1 = yes)
	//6 = TGATE (Gated time input?)
	//5 = TCKPS1 (clock prescaler 00 = 1 01 = 8 10 = 64 11 = 256
	//4	= TCKPS0
	//2 = TSYNC (Sync external clock?)
	//1 = TCS (Clock source 0 = Internal Clock (TOSC/4) 1 = External clock from T1CK pin

	//set PR1 to however many ticks it would take to make a 16kHz timer
	PR1 = (int)((dFcy+5) / 16000);

	IPC0bits.T1IP0 = 1;
	IPC0bits.T1IP1 = 0;
	IPC0bits.T1IP2 = 0;
	IFS0bits.T1IF = 0; //clear the flag
	IEC0bits.T1IE = 1; //enable interrupt
}

void InitPWM() {
// ============= Motor PWM ======================

    PTCONbits.PTSIDL = 0; //run in idle mode
	PTCONbits.PTOPS = 0; //post scale of 1
	PTCONbits.PTCKPS = 0; //input clock prescale of 1
	PTCONbits.PTMOD = 0b10; //continuous Up/Down counting mode - Center Aligned

    //how many ticks on the clock to go. We're in up/down mode so our
	//PWM period is twice what it would otherwise appear (have to go up, then back down)
	//and the input clock is dFcy or the instruction frequency clock
	//since our scalers are all one to one we just need to take the
	//instruction clock and divide by the target freq * 2. Also, the correct answer is one less (-1)
	//This sets the proper 50% bias point. So, for instance, if the answer we get is 624 then 624 means
	//50% duty cycle which is no actual work. 624 * 2 = 100% duty cycle which locks V+ on for the whole cycle
	//0 = no duty cycle which locks the output to ground
	PWM_BIAS = (int)((long)dFcy / (long)(dPWMFreq * 2)) - 1;
	MAX_PWM = PWM_BIAS << 1; //Twice PWM_BIAS
	PTPERbits.PTPER = PWM_BIAS;


    // SEVTCMP: Special Event Compare Count Register 
    // Phase of ADC capture set relative to PWM cycle: 0 offset and counting up
	// 
    SEVTCMPbits.SEVTDIR = 0;  //special event trigger on upwards count
	SEVTCMPbits.SEVTCMP = 3;
						// SEVTCMP cannot be 0 -> turns off trigger (Missing from doc)
						// SEVTCMP = 0b0000000000000011;  Pg. 339 in Family Reference Manual.
						// SEVTDIR = 0; A special event trigger will occur when the PWM time base is counting upwards and...
						// SEVTCMP = 000000000000010; If SEVTCMP == PTMR<14:0>, then a special event trigger happens.

	PWMCON1bits.PMOD1 = 0; //0 means PWM is in complimentary mode 1 = independent
	PWMCON1bits.PMOD2 = 0;
	PWMCON1bits.PMOD3 = 0;

	PWMCON1bits.PEN1H = 1; //1 means enable the high side PWM output
	PWMCON1bits.PEN2H = 1;
	PWMCON1bits.PEN3H = 1;

	PWMCON1bits.PEN1L = 1; //1 means enable the low side PWM output
	PWMCON1bits.PEN2L = 1;
	PWMCON1bits.PEN3L = 1;

	PWMCON2bits.SEVOPS = 0; //postscaler for special event trigger = 1 to 1
	PWMCON2bits.IUE = 0; //0 means sync changes to PDC registers. YOU WANT THIS!
	PWMCON2bits.OSYNC = 0; //output overrides happen essentially immediately
	PWMCON2bits.UDIS = 0; //0 = don't disable the ability to update duty cycle

	DTCON1bits.DTAPS = 0b11; // Prescaler of 8.

	//Have deadtime for DTA cycles. The prescaler multiplies this by 8 so it's really
	//DTA * 8 cycles of dFcy
	DTCON1bits.DTA = 7; //Maximum cycles for the deadtime is 63.
	//At 20Mhz instruction clock a prescale of 8 and a DTA of 7 will give a 2.8us deadtime

	//DTCON2 = 0; //this sets every dead time to use unit A which we configured above ^^^
	
//    FLTACON = 0;            // Pg. 343 in Family Reference Manual.  What to do when something happens on FAULT pin.  Nothing in this case.
							// I'm not using the fault pin.


        PDC1 = 0; // The PWM Duty for channel 1. the proper bias and stuff for this was calculated above
	PDC2 = 0; // PWM Duty for channel 2.
	PDC3 = 0; // PWM Duty for channel 3.
    // Center aligned PWM.  Oh heck ya.  It won't sample the A/D channels until the center of the ON time of the pwm, so no switching will happen while sampling, which cuts down on noise.

	PTCONbits.PTEN = 1; //gentlemen, start your engines!
}

void InitADC () {
// ============= ADC - Measure current 1, current 2, current 3, Throttle, temperature.
// AN0 = CURRENT 1
// AN1 = CURRENT 2
// AN3 = TEMPERATURE
// AN6 = THROTTLE
// AN7 = THROTTLE2

// Sampling triggered by PWM 

	// Note:  F.R.M. means Family Resource Manual for the dsPIC30F family of microcontrollers.
   
    ADCON1bits.FORM = 0;  // If FORM == 3, THE FORM WILL BE 1.15.  I'm not using that though. I'm doing unsigned int, since I'm doing fixed point math instead.
    // Motor Control PWM interval ends sampling and starts conversion
    ADCON1bits.SSRC = 3;  

    // Simultaneous Sample Select bit (only applicable when CHPS = 01 or 1x)
    // Samples CH0, CH1, CH2, CH3 simultaneously (when CHPS = 1x)
    // Samples CH0 and CH1 simultaneously (when CHPS = 01)
    ADCON1bits.SIMSAM = 1; 
 
    // Sampling begins immediately after last conversion completes. 
    // SAMP bit is auto set.
    ADCON1bits.ASAM = 1;  

    // Pg. 407 in F.R.M.
    // Samples CH0, CH1, CH2, CH3 simultaneously (when CHPS = 1x)
    ADCON2bits.CHPS = 2; // VCFG = 000; This selects the A/D High voltage as AVdd, and A/D Low voltage as AVss.
						 // SMPI = 0000; This makes an interrupt happen every time the A/D conversion process is done (for all 4 I guess, since they happen at the same time.)
						 // ALTS = 0; Always use MUX A input multiplexer settings
						 // BUFM = 0; Buffer configured as one 16-word buffer ADCBUF(15...0)

    // Pg. 408 in F.R.M.
    // A/D Conversion Clock Select bits = 8 * Tcy
    ADCON3bits.ADCS = 15;  // 

    // ADCHS: ADC Input Channel Select Register 
    // Pg. 409 in F.R.M.
    // CH0 positive input is AN6.  AN6 is throttle.
    ADCHSbits.CH0SA = 6;
    
	// CH1 positive input is AN0, CH2 positive input is AN1, CH3 positive input is AN2.
	ADCHSbits.CH123SA = 0;

	// CH0 negative input is Vref-.
	ADCHSbits.CH0NA = 0;

	// CH1, CH2, CH3 negative inputs are Vref-, which is AVss, which is Vss.  haha.
    ADCHSbits.CH123NA = 0;

    // ADPCFG: ADC Port Configuration Register 
    // Pg. 410 F.R.M.
	// using most all of the ADC ports except for the QEI pins
    ADPCFGbits.PCFG0 = 0;   // AN0 analog: current 1
    ADPCFGbits.PCFG1 = 0;   // AN1 analog: current 2
    ADPCFGbits.PCFG2 = 0;   // AN2 analog: motor temp
    ADPCFGbits.PCFG3 = 0;   // AN3 analog: HV sense
    ADPCFGbits.PCFG4 = 1;   //QEI
    ADPCFGbits.PCFG5 = 1;   //QEI
    ADPCFGbits.PCFG6 = 0;   // AN6 analog: Throttle1
    ADPCFGbits.PCFG7 = 0;   // AN7 analog: Throttle2
    ADPCFGbits.PCFG8 = 1;   


    // ADCSSL: ADC Input Scan Select Register 
	// This makes no sense to me.
    //ADCSSL = 0; // Pg. 410 F.R.M.
	// I think it sets the order that the A/D inputs are done.  But I'm doing 4 all at the same time, so set it to 0?

    // Turn on A/D module
    ADCON1bits.ADON = 1; // Pg. 416 in F.R.M.  Under "Enabling the Module"
						 // ** It's important to set all the bits above before turning on the A/D module. **
						 // Now the A/D conversions start happening once ADON == 1.
	IEC0bits.ADIE = 1;	 // Enable interrupts to happen when a A/D conversion is complete. Pg. 148 of F.R.M.  
}

//I haven't double checked the stuff in this function. Might still need work before it is used
void InitQEI() {
	// QEICON starts as all zeros.
	// The Quadrature Encoder Interface.  This is for enabling the encoder.
	QEICONbits.QEIM = 0b101; 	// enable QEI x2 mode with position counter reset by MAXCNT
	QEICONbits.PCDOUT = 0; 	 	// Position Counter Direction Status Output is disabled. No reason to output this
	QEICONbits.POSRES = 0;		// We're not using the index pulse

	DFLTCONbits.CEID = 1; 		// Interrupts due to position count errors disabled
	DFLTCONbits.QEOUT = 1; 		// Digital filter outputs enabled.  QEA, QEB, and INDEX. 0 means normal pin operation for all 3.
	DFLTCONbits.QECK = 0b010; 	// clock prescaler of 4. So, QEA or QEB or INDEX must be high or low for a time of 4*3 Tcy's. 
	DFLTCONbits.IMV = 0b00;  	// INDEX pulse happens when QEA is low.
        
}


//see if checksum is valid. If so we do nothing.
//otherwise set all values to defaults
void InitEEPROM(void) {
	int temp;
	temp = ee_verify_checksum();
	if (temp == 0) {
		ee_set_use_devices(EE_USE_MAIN_CONTACTOR |
			EE_USE_PRECHARGE | EE_USE_POT_THROTTLE |
			EE_USE_MAIN_CONTACTOR_SENSE);
		ee_set_precharge_time(2000); //2 seconds
		ee_set_rotor_time_const(1000); //in .1MS increments
		ee_set_encoder_count(512);
		ee_set_current_gain(15); //current per ADC in 1/10A
		ee_set_current_bias(512); //the 1/2 way mark
		ee_set_voltage_gain(50); //in 1/10 volt
		ee_set_motor_poles(2); //probably most likely value
		ee_set_max_volt(350);
		ee_set_min_volt(280);
		ee_set_max_amps(400);
		ee_set_motor_volt(250);
		ee_set_motor_hertz(60);
		ee_set_overheat_ctrl(0);
		ee_set_overheat_motor(0);
		ee_set_last_faults(0);
		ee_set_throttle_min(40);
		ee_set_throttle_max(980);
		ee_set_canbus_speed(EE_CANBUS_SPEED_250K);
		ee_set_canbus_id(0x00000555);
		ee_save_checksum();
	}
}

